use crate::controller::error::Error;
use crate::{dto, mq, service::ServiceSet};
use std::sync::Arc;
use wanmesh_common::dto::ResponseCode;
use wanmesh_common::error::ResponseError;

pub struct EndpointPeerController {
    service_set: Arc<ServiceSet>,
    event_handler: EventHandler,
}

impl EndpointPeerController {
    pub fn new(service_set: Arc<ServiceSet>, mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self {
            service_set,
            event_handler: EventHandler::new(mq_client_set),
        }
    }

    pub async fn add(
        &self,
        request: &dto::endpoint_peer::AddRequest,
    ) -> Result<dto::core::endpoint_peer::action::AddResponseData, Error> {
        let _ = self
            .service_set
            .endpoint_manager
            .get(request.inner.endpoint_id.as_str())
            .await?;

        let _ = self
            .service_set
            .endpoint_manager
            .get(request.inner.peer_endpoint_id.as_str())
            .await?;

        let endpoint = self
            .service_set
            .endpoint_manager
            .get(request.inner.endpoint_id.as_str())
            .await?;

        let peer_endpoint = self
            .service_set
            .endpoint_manager
            .get(request.inner.endpoint_id.as_str())
            .await?;

        if endpoint.endpoint_id == request.inner.peer_endpoint_id {
            return Err(Error::ResponseError(ResponseError {
                code: ResponseCode::BAD_REQUEST,
                message: "endpoint_id与peer_endpoint_id不能相同".to_string(),
                backtrace: Default::default(),
            }));
        }

        if endpoint.inner.endpoint_type != peer_endpoint.inner.endpoint_type {
            return Err(Error::ResponseError(ResponseError {
                code: ResponseCode::BAD_REQUEST,
                message: format!(
                    "endpoint.endpoint_type={}与peer_endpoint.endpoint_type={}不相同，endpoint_id={}, peer_endpoint_endpoint_id={}",
                    endpoint.inner.endpoint_type,
                    peer_endpoint.inner.endpoint_type,
                    request.inner.endpoint_id,
                    request.inner.peer_endpoint_id
                ),
                backtrace: Default::default(),
            }));
        }

        let data = self
            .service_set
            .endpoint_peer_manager
            .add(&request.inner)
            .await?;

        if let Some(wg_add_params) = &request.wireguard {
            self.service_set
                .endpoint_peer_wireguard_config_manager
                .add(data.endpoint_peer_id.as_str(), wg_add_params)
                .await?;
        }

        self.event_handler
            .spawn_handle_setup_event(
                endpoint.inner.node_id.as_str(),
                dto::event::endpoint::peer::SetupEventPayload {
                    endpoint_peer_id: data.endpoint_peer_id.clone(),
                },
            );

        Ok(data)
    }

    pub async fn list(
        &self,
        request: &dto::endpoint_peer::ListRequest,
    ) -> Result<dto::endpoint_peer::ListResponseData, Error> {
        let total_count = self
            .service_set
            .endpoint_peer_manager
            .count(&request.filter)
            .await?;
        if total_count == 0 {
            return Ok(dto::endpoint_peer::ListResponseData {
                total_count: 0,
                list: Vec::new(),
            });
        }

        let list = self
            .service_set
            .endpoint_peer_manager
            .list(&request.filter, request.offset, request.limit)
            .await?;

        Ok(dto::endpoint_peer::ListResponseData { total_count, list })
    }

    pub async fn list_info(
        &self,
        request: &dto::endpoint_peer::ListRequest,
    ) -> Result<dto::endpoint_peer::ListInfoResponseData, Error> {
        let total_count = self
            .service_set
            .endpoint_peer_manager
            .count(&request.filter)
            .await?;
        if total_count == 0 {
            return Ok(dto::endpoint_peer::ListInfoResponseData {
                total_count: 0,
                list: Vec::new(),
            });
        }

        let list = self
            .service_set
            .endpoint_peer_manager
            .list_info(&request.filter, request.offset, request.limit)
            .await?;

        Ok(dto::endpoint_peer::ListInfoResponseData { total_count, list })
    }

    pub async fn get(
        &self,
        request: &dto::endpoint_peer::GetRequest,
    ) -> Result<dto::core::endpoint_peer::EndpointPeerItem, Error> {
        let data = self
            .service_set
            .endpoint_peer_manager
            .get(request.endpoint_peer_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn get_info(
        &self,
        request: &dto::endpoint_peer::GetRequest,
    ) -> Result<dto::endpoint_peer::EndpointPeerInfo, Error> {
        let data = self
            .service_set
            .endpoint_peer_manager
            .get_info(request.endpoint_peer_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn try_get_info(
        &self,
        request: &dto::endpoint_peer::GetRequest,
    ) -> Result<Option<dto::endpoint_peer::EndpointPeerInfo>, Error> {
        let data = self
            .service_set
            .endpoint_peer_manager
            .try_get_info(request.endpoint_peer_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn delete(&self, request: &dto::endpoint_peer::DeleteRequest) -> Result<(), Error> {
        let peer = match self
            .service_set
            .endpoint_peer_manager
            .try_get(request.endpoint_peer_id.as_str())
            .await?
        {
            None => return Ok(()),
            Some(v) => v,
        };

        self.service_set
            .endpoint_peer_manager
            .delete(request.endpoint_peer_id.as_str())
            .await?;

        let endpoint = match self
            .service_set
            .endpoint_manager
            .try_get(peer.inner.endpoint_id.as_str())
            .await?
        {
            None => return Ok(()),
            Some(v) => v,
        };

        self.event_handler
            .spawn_handle_delete_event(
                endpoint.inner.node_id.as_str(),
                dto::event::endpoint::peer::DeleteEventPayload {
                    endpoint_peer_id: request.endpoint_peer_id.clone(),
                },
            );

        Ok(())
    }
}


pub struct EventHandler {
    mq_client_set: Arc<mq::client::ClientSet>,
}

impl EventHandler {
    pub fn new(mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self { mq_client_set }
    }
    pub fn spawn_handle_setup_event(
        &self,
        node_id: &str,
        payload: dto::event::endpoint::peer::SetupEventPayload,
    ) {
        let mq_client_set = self.mq_client_set.clone();
        let node_id = node_id.to_string();
        tokio::spawn(async move {
            if let Err(e) = mq_client_set
                .node
                .endpoint
                .peer
                .publish_setup_event(node_id.as_str(), payload)
                .await
            {
                log::error!("{:?}", e);
            }
        });
    }

    pub fn spawn_handle_delete_event(
        &self,
        node_id: &str,
        payload: dto::event::endpoint::peer::DeleteEventPayload,
    ) {
        let mq_client_set = self.mq_client_set.clone();
        let node_id = node_id.to_string();
        tokio::spawn(async move {
            if let Err(e) = mq_client_set
                .node
                .endpoint
                .peer
                .publish_delete_event(node_id.as_str(), payload)
                .await
            {
                log::error!("{:?}", e);
            }
        });
    }
}
